<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>
body {
  font-family: Helvetica, arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  padding-top: 10px;
  padding-bottom: 10px;
  background-color: white;
  padding: 30px; }

body > *:first-child {
  margin-top: 0 !important; }
body > *:last-child {
  margin-bottom: 0 !important; }

a {
  color: #4183C4; }
a.absent {
  color: #cc0000; }
a.anchor {
  display: block;
  padding-left: 30px;
  margin-left: -30px;
  cursor: pointer;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0; }

h1, h2, h3, h4, h5, h6 {
  margin: 20px 0 10px;
  padding: 0;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
  cursor: text;
  position: relative; }

h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
  background: url() no-repeat 10px center;
  text-decoration: none; }

h1 tt, h1 code {
  font-size: inherit; }

h2 tt, h2 code {
  font-size: inherit; }

h3 tt, h3 code {
  font-size: inherit; }

h4 tt, h4 code {
  font-size: inherit; }

h5 tt, h5 code {
  font-size: inherit; }

h6 tt, h6 code {
  font-size: inherit; }

h1 {
  font-size: 28px;
  color: black; }

h2 {
  font-size: 24px;
  border-bottom: 1px solid #cccccc;
  color: black; }

h3 {
  font-size: 18px; }

h4 {
  font-size: 16px; }

h5 {
  font-size: 14px; }

h6 {
  color: #777777;
  font-size: 14px; }

p, blockquote, ul, ol, dl, li, table, pre {
  margin: 15px 0; }

hr {
  background: transparent url() repeat-x 0 0;
  border: 0 none;
  color: #cccccc;
  height: 4px;
  padding: 0;
}

body > h2:first-child {
  margin-top: 0;
  padding-top: 0; }
body > h1:first-child {
  margin-top: 0;
  padding-top: 0; }
  body > h1:first-child + h2 {
    margin-top: 0;
    padding-top: 0; }
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
  margin-top: 0;
  padding-top: 0; }

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
  margin-top: 0;
  padding-top: 0; }

h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
  margin-top: 0; }

li p.first {
  display: inline-block; }
li {
  margin: 0; }
ul, ol {
  padding-left: 30px; }

ul :first-child, ol :first-child {
  margin-top: 0; }

dl {
  padding: 0; }
  dl dt {
    font-size: 14px;
    font-weight: bold;
    font-style: italic;
    padding: 0;
    margin: 15px 0 5px; }
    dl dt:first-child {
      padding: 0; }
    dl dt > :first-child {
      margin-top: 0; }
    dl dt > :last-child {
      margin-bottom: 0; }
  dl dd {
    margin: 0 0 15px;
    padding: 0 15px; }
    dl dd > :first-child {
      margin-top: 0; }
    dl dd > :last-child {
      margin-bottom: 0; }

blockquote {
  border-left: 4px solid #dddddd;
  padding: 0 15px;
  color: #777777; }
  blockquote > :first-child {
    margin-top: 0; }
  blockquote > :last-child {
    margin-bottom: 0; }

table {
  padding: 0;border-collapse: collapse; }
  table tr {
    border-top: 1px solid #cccccc;
    background-color: white;
    margin: 0;
    padding: 0; }
    table tr:nth-child(2n) {
      background-color: #f8f8f8; }
    table tr th {
      font-weight: bold;
      border: 1px solid #cccccc;
      margin: 0;
      padding: 6px 13px; }
    table tr td {
      border: 1px solid #cccccc;
      margin: 0;
      padding: 6px 13px; }
    table tr th :first-child, table tr td :first-child {
      margin-top: 0; }
    table tr th :last-child, table tr td :last-child {
      margin-bottom: 0; }

img {
  max-width: 100%; }

span.frame {
  display: block;
  overflow: hidden; }
  span.frame > span {
    border: 1px solid #dddddd;
    display: block;
    float: left;
    overflow: hidden;
    margin: 13px 0 0;
    padding: 7px;
    width: auto; }
  span.frame span img {
    display: block;
    float: left; }
  span.frame span span {
    clear: both;
    color: #333333;
    display: block;
    padding: 5px 0 0; }
span.align-center {
  display: block;
  overflow: hidden;
  clear: both; }
  span.align-center > span {
    display: block;
    overflow: hidden;
    margin: 13px auto 0;
    text-align: center; }
  span.align-center span img {
    margin: 0 auto;
    text-align: center; }
span.align-right {
  display: block;
  overflow: hidden;
  clear: both; }
  span.align-right > span {
    display: block;
    overflow: hidden;
    margin: 13px 0 0;
    text-align: right; }
  span.align-right span img {
    margin: 0;
    text-align: right; }
span.float-left {
  display: block;
  margin-right: 13px;
  overflow: hidden;
  float: left; }
  span.float-left span {
    margin: 13px 0 0; }
span.float-right {
  display: block;
  margin-left: 13px;
  overflow: hidden;
  float: right; }
  span.float-right > span {
    display: block;
    overflow: hidden;
    margin: 13px auto 0;
    text-align: right; }

code, tt {
  margin: 0 2px;
  padding: 0 5px;
  white-space: nowrap;
  border: 1px solid #eaeaea;
  background-color: #f8f8f8;
  border-radius: 3px; }

pre code {
  margin: 0;
  padding: 0;
  white-space: pre;
  border: none;
  background: transparent; }

.highlight pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px; }

pre {
  background-color: #f8f8f8;
  border: 1px solid #cccccc;
  font-size: 13px;
  line-height: 19px;
  overflow: auto;
  padding: 6px 10px;
  border-radius: 3px; }
  pre code, pre tt {
    background-color: transparent;
    border: none; }

sup {
    font-size: 0.83em;
    vertical-align: super;
    line-height: 0;
}
* {
	-webkit-print-color-adjust: exact;
}
@media screen and (min-width: 914px) {
    body {
        width: 854px;
        margin:0 auto;
    }
}
@media print {
	table, pre {
		page-break-inside: avoid;
	}
	pre {
		word-wrap: break-word;
	}
}
</style>
<title>包开发</title>

</head>
<body>
<h1>包开发</h1>

<ul>
<li><a href="#introduction">简介</a></li>
<li><a href="#creating-a-package">创建一个包</a></li>
<li><a href="#package-structure">包结构</a></li>
<li><a href="#service-providers">服务提供商</a></li>
<li><a href="#package-conventions">包约定</a></li>
<li><a href="#development-workflow">开发流程</a></li>
<li><a href="#package-routing">包路由</a></li>
<li><a href="#package-configuration">包配置</a></li>
<li><a href="#package-migrations">包迁移</a></li>
<li><a href="#package-assets">包资源</a></li>
<li><a href="#publishing-packages">发布包</a></li>
</ul>


<p><a name="introduction"></a></p>

<h2>简介</h2>

<p>包主要用于向 Laravel 中添加功能。包既可以是用于处理日期的 <a href="https://github.com/briannesbitt/Carbon">Carbon</a>，也可以是一整套 像 <a href="https://github.com/Behat/Behat">Behat</a> 这样的 BDD 测试框架。</p>

<p>当然，包有不同的类型。有一些包是独立的，意味着可以在任何框架中使用而不仅是 Laravel。Carbon 和 Behat 都是独立包的例子，他们都可以通过 <code>composer.json</code> 声明在 Laravel 中被使用。</p>

<p>另一方面，其他包仅针对 Laravel 使用。在早期的 Laravel 版本中，这种类型的包被称为 "bundles"。这些包可能包含路由、控制器、视图、配置以及迁移仅用于扩展一个 Laravel 应用程序。因为开发一个独立的包没有特别的流程，本指南将主要涵盖只针对 Laravel 的包的开发。</p>

<p>所有 Laravel 的包由 <a href="http://packagist.org">Packagist</a> 和 <a href="http://getcomposer.org">Composer</a> 分发，所以学习这些 PHP 包分发工具是必不可少的。</p>

<p><a name="creating-a-package"></a></p>

<h2>创建一个包</h2>

<p>最简单的方法来创建一个在 Laravel 中使用的包是 <code>workbench</code> Artisan 命令。首先，您需要在 <code>app/config/workbench.php</code> 文件中设置一些选项。在这个文件中，您会发现一个 <code>name</code> 以及 <code>email</code> 选项。这些值将被使用于为您的新包生成一个 <code>composer.json</code> 文件。一旦您已经提供了这些值，您就可以构建一个工作台包了！</p>

<p><strong>关于工作台 Artisan 命令</strong></p>

<pre><code>php artisan workbench vendor/package --resources
</code></pre>

<p>供应商的名字可以用来区分您的包和其他来自不同的作者但相同的名字的包。比如，我（Taylor Otwell）想创建一个名为 "Zapper" 的包，那么供应商的名字可以为 <code>Taylor</code> 同时包名可以为 <code>Zapper</code>。默认情况下，工作台将创建不可知的包，但是 <code>resources</code> 命令告诉工作台产生带有 Laravel 相关目录的包，比如 <code>migrations</code>, <code>views</code>, <code>config</code> 等。</p>

<p>一旦 <code>workbench</code>  命令已经被执行，您的包将在您安装的 Laravel 的 <code>workbench</code> 目录。接下来，您应该注册您的包所创建的 <code>ServiceProvider</code>。您可以注册供应商通过添加它到 <code>app/config/app.php</code> 文件中的 <code>providers</code> 数组。这将告诉 Laravel 在应用程序启动时加载您的包。服务供应商使用一个 <code>[Package]ServiceProvider</code> 命名惯例。所以使用上面那个例子，您应该添加 <code>Taylor\Zapper\ZapperServiceProvider</code> 到 <code>providers</code> 数组。</p>

<p>一旦供应商被注册，您就可以开发您的包了！但是，在开始之前，您可能希望浏览下面的章节对包结构和开发流程进行更深入的了解。</p>

<p><a name="package-structure"></a></p>

<h2>包结构</h2>

<p>当使用 <code>workbench</code> 命令，您的包将按照惯例以允许包与 Laravel 框架的其他部分协调工作的方式进行设置：</p>

<p><strong>基本包的目录结构</strong></p>

<pre><code>/src
    /Vendor
        /Package
            PackageServiceProvider.php
    /config
    /lang
    /migrations
    /views
/tests
/public
</code></pre>

<p>让我进一步探索这个结构。<code>src/Vendor/Package</code> 目录是所有包的类的主目录，包括 <code>ServiceProvider</code>。<code>config</code>, <code>lang</code>, <code>migrations</code> 以及 <code>views</code> 目录和您想的一样，包含相关的包的资源。包可以有任何这些资源，就像一个常规的应用程序一样。</p>

<p><a name="service-providers"></a></p>

<h2>服务提供商</h2>

<p>服务提供商是包的一个简单的引导类。默认情况下，它包含两个函数，<code>boot</code> 和 <code>register</code>。在这些函数中，您可以做这些事情，比如包含一个路由文件，注册 IoC 容器的绑定，附加事件或者任何您想做的事情。</p>

<p><code>register</code> 函数当服务提供商被注册时立即调用，<code>boot</code> 函数只有在一个请求被路由之前调用。所以，如果在服务提供商中的行为依赖于另一个已注册的服务提供商，或者您重写另一个提供商的服务，您应该使用 <code>boot</code> 函数。</p>

<p>当使用 <code>workbench</code> 创建一个包，<code>boot</code> 命令将已经包含一个行为：</p>

<pre><code>$this-&gt;package('vendor/package');
</code></pre>

<p>这个函数允许 Laravel 知道怎样正确地为您的应用程序加载视图、配置文件以及其他资源。一般来说，您没有必要改变这行代码，因为它将按照工作台管理创建一个包。</p>

<p><a name="package-conventions"></a></p>

<h2>包约定</h2>

<p>当从一个包中使用资源，比如配置选项或者视图，一个双冒号语法通常会被使用：</p>

<p><strong>从包中加载一个视图</strong></p>

<pre><code>return View::make('package::view.name');
</code></pre>

<p><strong>检索包中的一个配置选项</strong></p>

<pre><code>return Config::get('package::group.option');
</code></pre>

<blockquote><p><strong>注意:</strong> 如果您的包包含迁移，考虑为您的迁移加上包名的前缀避免潜在的与其他包的类名冲突。</p></blockquote>

<p><a name="development-workflow"></a></p>

<h2>开发流程</h2>

<p>当开发一个包，在应用程序的上下文环境中开发是有用的，允许您轻松地查看和试验您的模板等。所以为了起步，安装一个全新的 Laravel 框架，然后使用 <code>workbench</code> 命令创建您的包结构。</p>

<p>在使用 <code>workbench</code> 命令创建您的包之后，您可以在 <code>workbench/[vendor]/[package]</code> 目录运行 <code>git init</code> 以及 <code>git push</code>。这将允许您方便在应用程序上下文环境中开发包而不必陷入使用 <code>composer update</code> 命令。</p>

<p>因为您的包在 <code>workbench</code> 目录，您可能像知道 Composer 怎样知道自动加载您包中的文件。当 <code>workbench</code> 目录存在的时候，Laravel 将智能地扫描包，当应用程序启动的时候加载它们的 Composer 自动加载文件。</p>

<p>如果你需要重新生成您的包的自动加载文件，您可以使用 <code>php artisan dump-autoload</code> 命令。这个命令将为您的项目以及如何您创建的工作台重新生成自动加载文件。</p>

<p><strong>运行 Artisan 自动加载命令</strong></p>

<pre><code>php artisan dump-autoload
</code></pre>

<p><a name="package-routing"></a></p>

<h2>包路由</h2>

<p>在之前版本的 Laravel 中，一个 <code>handles</code> 条款用于指明一个包可以响应哪些 URIs。但是，在 Laravel 4，一个包可以响应任何 URI。为您的包加载一个路由文件，简单地在服务提供商的 <code>boot</code> 函数中使用 <code>include</code> 它。</p>

<p><strong>从一个服务提供商中包含一个路由</strong></p>

<pre><code>public function boot()
{
    $this-&gt;package('vendor/package');

    include __DIR__.'/../../routes.php';
}
</code></pre>

<blockquote><p><strong>注意:</strong> 如果您的包使用控制器，您需要确认在 <code>composer.json</code> 文件的自动加载段中正确地配置。</p></blockquote>

<p><a name="package-configuration"></a></p>

<h2>包配置</h2>

<p>一些包可能包含配置文件。这些文件应该以常规应用程序配置文件的方式被定义。并且，在使用默认的 <code>$this-&gt;package</code> 方法在服务供应商中注册资源，可以使用通常的双冒号语法：</p>

<p><strong>访问包配置文件</strong></p>

<pre><code>Config::get('package::file.option');
</code></pre>

<p>然而，如果您的包仅包含一个单独的配置文件，您可以简单地命名这个文件为 <code>config.php</code>。当这样做，您可以直接访问这些选项，而不需要指定文件名。</p>

<p><strong>访问包的单文件配置</strong></p>

<pre><code>Config::get('package::option');
</code></pre>

<h3>配置文件层叠</h3>

<p>当其他的开发者安装了您的包，他们可能希望重写一些您配置中的选项。然而，如果他们改变包的源代码的值，他们需要在 Composer 升级这个包的时候再次重写一次。相反，<code>config:publish</code> Artisan 命令可以被使用：</p>

<p><strong>执行配置发布指令</strong></p>

<pre><code>php artisan config:publish vendor/package
</code></pre>

<p>当这条命令被执行，应用程序的配置文件将被拷贝到 <code>app/config/packages/vendor/package</code> 可以被开发者安全地修改。</p>

<blockquote><p><strong>注意:</strong> 开发者也可以为您的包创建指定应用环境的配置文件，在 <code>app/config/packages/vendor/package/environment</code> 目录下。</p></blockquote>

<p><a name="package-migrations"></a></p>

<h2>包迁移</h2>

<p>您可以轻松的为您的包创建和运行迁移。在工作台为您的包创建一个迁移，请使用 <code>--bench</code> 选项：</p>

<p><strong>为工作台包创建迁移</strong></p>

<pre><code>php artisan migrate:make create_users_table --bench="vendor/package"
</code></pre>

<p><strong>为工作台包运行迁移</strong></p>

<pre><code>php artisan migrate --bench="vendor/package"
</code></pre>

<p>为一个通过 Composer 安装到 <code>vendor</code> 目录的已完成的包运行迁移，您可以使用 <code>--package</code> 选项：</p>

<p><strong>为一个已安装的包运行迁移</strong></p>

<pre><code>php artisan migrate --package="vendor/package"
</code></pre>

<p><a name="package-assets"></a></p>

<h2>包资源</h2>

<p>一些包可能包含JavaScript、CSS、以及图片资源。但是我们无法通过 <code>vendor</code> 或 <code>workbench</code> 目录访问这些资源，所以我们需要一种方法移动这些资源文件转移到应用程序的 <code>public</code> 目录。<code>asset:publish</code> 命令将为您做这些工作：</p>

<p><strong>转移包资源到Public目录</strong></p>

<pre><code>php artisan asset:publish

php artisan asset:publish vendor/package
</code></pre>

<p>如果包仍在 <code>workbench</code>，请使用 <code>--bench</code> 选项：</p>

<pre><code>php artisan asset:publish --bench="vendor/package"
</code></pre>

<p>这个命令将根据供应商和包名转移资源文件到 <code>public/packages</code> 目录。因此，一个名为 <code>userscape/kudos</code> 的包的资源文件将转移到  <code>public/packages/userscape/kudos</code> 目录下。使用这个资源发布惯例将允许您在包的视图中安全的使用资源路径。</p>

<p><a name="publishing-packages"></a></p>

<h2>发布包</h2>

<p>当您的包准备发布的时候，您应该提交包到 <a href="http://packagist.org">Packagist</a> 仓库。如果这个包只适用于 Laravel，可以考虑在包的 <code>composer.json</code> 文件中添加一个 <code>laravel</code> 标记。</p>

<p>同时，对版本号做标记将是有礼貌且有帮助的，使开发者勊只在他们的 <code>composer.json</code> 文件中依赖稳定的版本。如果没有一个稳定的版本，可以考虑使用 <code>branch-alias</code> Composer 指令。</p>

<p>一旦您的包已发布，请在通过 <code>workbench</code> 创建的应用程序中继续发展它，在包发布之后进行持续的开发是一个很好的方式。</p>

<p>有一些组织选择选择为他们的开发者开放他们私有的代码仓库。如果您对此感兴趣，参考 Composer 团队提供的 <a href="http://github.com/composer/satis">Satis</a> 项目的文档。</p>
</body>
</html>